/*
 * $Id: MainWindow.java,v 1.13 2003/06/29 11:16:19 znamenacek Exp $
 *
 * Copyright (c) Tomas Znamenacek
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

package net.sf.vofce.gui;

import javax.swing.AbstractAction;
import javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.ButtonGroup;
import javax.swing.JSplitPane;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.KeyStroke;
import javax.swing.SwingConstants;
import javax.swing.UIManager;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.util.ResourceBundle;

import net.sf.vofce.project.Config;
import net.sf.vofce.project.Project;
import net.sf.vofce.util.ExtensionFilter;

import org.apache.log4j.Category;

/**
 * Main vofce window.
 *
 * @author Tomas Znamenacek
 */
public class MainWindow extends JFrame implements ActionListener {

	protected static final int YES = 0;
	protected static final int NO = 1;
	protected static final int CANCEL = 2;

	/** Logger instance. */
	protected static Category logger = Category.getRoot();

	/**
	 * The width ratio between the graphs and sidebar.
	 */
	public static final double SPLIT_RATIO = 0.8d;
	
	/** Localised resource strings. */
	protected static ResourceBundle resources;
	
	/** Instance of the configuration system. */
	protected static Config config = Config.getInstance();

	/** Instance of the project. */
	protected static Project project = Project.getInstance();

	/** The file with current project. */
	protected File file;
	
	/**
	 * Creates the main window.
	 * Sets main window properties (size, location, ...), loads localised
	 * strings.
	 */
	public MainWindow() {

		//
		// Main window properties
		// 
		
		aboutBox = new AboutBox(this);
		exportDialog = new ExportDialog(this);
		inputDialog = new InputDialog(this);

		Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();
		Dimension ourDim = new Dimension();

		ourDim.height = (int) (screenDim.height * 0.75);
		ourDim.width = (int) (screenDim.width * 0.75);
		
		setSize(ourDim);
		setLocation((int) ((screenDim.width - ourDim.width) / 2), (int) ((screenDim.height - ourDim.height) / 2));

		setTitle(resources.getString("window.title"));
		setResizable(true);
		
		getContentPane().setLayout(new BorderLayout());

		// Set window-close hook
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent we) {
				closeWindow();
			}
		});

		setupComponents();
		
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setVisible(true);
	}

	/**
	 * Lays out the UI components that make up the main window.
	 */
	private void setupComponents() {

		//
		// Application menu
		//
		
		JMenu fileMenu = new JMenu(resources.getString("menu.file"));
		
		JMenuItem newMenuItem = fileMenu.add(newAction);
		newMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, java.awt.Event.CTRL_MASK));
		
		JMenuItem openMenuItem = fileMenu.add(openAction);
		openMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, java.awt.Event.CTRL_MASK));
		
		JMenuItem saveMenuItem = fileMenu.add(saveAction);
		saveMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, java.awt.Event.CTRL_MASK));
		
		JMenuItem saveAsMenuItem = fileMenu.add(saveAsAction);
	
		JMenuItem exportMenuItem = fileMenu.add(exportAction);
		exportMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, java.awt.Event.CTRL_MASK));
		
		fileMenu.addSeparator();

		JMenuItem exitMenuItem = fileMenu.add(closeAction);
		exitMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, java.awt.Event.CTRL_MASK));

		// Help menu
		JMenu helpMenu = new JMenu(resources.getString("menu.help"));
		
		JMenuItem aboutMenuItem = helpMenu.add(aboutAction);
		aboutMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0));
	
		// Dump it out
		JMenuBar menuBar = new JMenuBar();
		
		menuBar.add(fileMenu);
		
		menuBar.add(Box.createHorizontalGlue());
		menuBar.add(helpMenu);
		
		setJMenuBar(menuBar);

		//
		// Toolbar
		//
	
		JToolBar toolbar = new JToolBar();
		toolbar.setFloatable(false);
		toolbar.setOrientation(SwingConstants.HORIZONTAL);
	
		JButton newButton = toolbar.add(newAction);
		newButton.setToolTipText(resources.getString("tooltip.new"));
		
		JButton openButton = toolbar.add(openAction);
		openButton.setToolTipText(resources.getString("tooltip.open"));
	
		JButton saveButton = toolbar.add(saveAction);
		saveButton.setToolTipText(resources.getString("tooltip.save"));
		
		JButton saveAsButton = toolbar.add(saveAsAction);
		saveAsButton.setToolTipText(resources.getString("tooltip.saveAs"));
		
		JButton exportButton = toolbar.add(exportAction);
		exportButton.setToolTipText(resources.getString("tooltip.export"));
		
		getContentPane().add(toolbar, BorderLayout.NORTH);

		//
		// Main window layout
		//
		
		splitter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
		
		sidebar = new FunctionList();
		graphPanel = GraphPanel.getInstance();

		splitter.setLeftComponent(graphPanel);
		splitter.setRightComponent(sidebar);
		splitter.setDividerLocation(SPLIT_RATIO);
		splitter.setResizeWeight(SPLIT_RATIO);
		
		getContentPane().add(splitter, BorderLayout.CENTER);

		sidebar.setMinimumSize(new Dimension(200, 200));
		sidebar.setPreferredSize(sidebar.getMinimumSize());
		graphPanel.setMinimumSize(new Dimension(300, 200));

		graphPanel.requestFocus();

		//
		// Viewport tools
		//
		
		toolbar.addSeparator();
		
		JButton resetButton = toolbar.add(resetViewport);
		resetButton.setToolTipText(resources.getString("tooltip.reset"));
		
		ButtonGroup tools = new ButtonGroup();

		JToggleButton moveToolButton = new JToggleButton(moveTool);
		moveToolButton.setToolTipText(resources.getString("tooltip.move"));
		tools.add(moveToolButton);
		toolbar.add(moveToolButton);
		moveToolButton.doClick();
		
		JToggleButton zoomToolButton = new JToggleButton(zoomTool);
		zoomToolButton.setToolTipText(resources.getString("tooltip.zoom"));
		tools.add(zoomToolButton);
		toolbar.add(zoomToolButton);

		String[] graphingModes = {resources.getString("grmode.connected"), resources.getString("grmode.dotted")};
		JComboBox modeSwitch = new JComboBox(graphingModes);
		modeSwitch.setMaximumSize(new Dimension(100, 100));
		modeSwitch.addActionListener(this);
		modeSwitch.setToolTipText(resources.getString("tooltip.grmode"));
			
		toolbar.addSeparator();
		toolbar.add(modeSwitch);
		toolbar.add(Box.createHorizontalGlue());
	}

	/** Receives the combobox changes. */
	public void actionPerformed(ActionEvent e) {

		JComboBox cb = (JComboBox) e.getSource();
		String graphingMode = (String) cb.getSelectedItem();

		if (graphingMode.equals(resources.getString("grmode.dotted"))) {
			graphPanel.setGraphingMode(graphPanel.LAYER_DOTTED);
		} else {
			graphPanel.setGraphingMode(graphPanel.LAYER_LINED);
		}
	}
	
	/**
	 * Called when the main window changes bounds.
	 * We use it to adjust the splitter.
	 */
	public void setBounds(int x, int y, int width, int height) {

		super.setBounds(x, y, width, height);
		
		if (splitter != null) {
			splitter.setDividerLocation(SPLIT_RATIO);
		}
	}

	/**
	 * Closes the window and exits the application.
	 */
	private void closeWindow() {

		config.saveToFile();
		System.exit(0);
	}
	
	//
	// GUI components
	//

	/** The about dialog. */
	private AboutBox aboutBox;

	/** Export dialog. */
	private ExportDialog exportDialog;

	/** The dialog for entering the functions. */
	private InputDialog inputDialog;
	
	/** Panel with all the drawings. */
	private GraphPanel graphPanel;
	
	/** Sidebar with controls. */
	private FunctionList sidebar;

	/** The split pane splitting function graphs and function list. */
	private JSplitPane splitter;
	
	/**
	 * Returns image associated with given resource id.
	 */
	private ImageIcon getIcon(String id) {

		return new ImageIcon(getClass().getResource("/" + id));
	}

	//
	// ACTIONS
	//

	/** Display the About box. */
	private AbstractAction aboutAction = new AbstractAction(
		resources.getString("menu.help.about"),
		getIcon("about.gif")
	) {
		public void actionPerformed(ActionEvent e) {
			aboutBox.show();
		}
	};
	
	/** New project. */
	private AbstractAction newAction = new AbstractAction(
		resources.getString("menu.file.new"),
		getIcon("new.gif")
	) {
		public void actionPerformed(ActionEvent e) {
			clearProject();
		}
	};
	
	/** Open project. */
	private AbstractAction openAction = new AbstractAction(
		resources.getString("menu.file.open"),
		getIcon("open.gif")
	) {
		public void actionPerformed(ActionEvent e) {
			loadProject();
		}
	};
	
	/** Save the project. */
	private AbstractAction saveAction = new AbstractAction(
		resources.getString("menu.file.save"),
		getIcon("save.gif")
	) {
		public void actionPerformed(ActionEvent e) {
			saveProject(false);
		}
	};
	
	/** Save the project to some file. */
	private AbstractAction saveAsAction = new AbstractAction(
		resources.getString("menu.file.saveAs"),
		getIcon("saveas.gif")
	) {
		public void actionPerformed(ActionEvent e) {
			saveProject(true);
		}
	};
	
	/** Export. */
	private AbstractAction exportAction = new AbstractAction(
		resources.getString("menu.file.export"),
		getIcon("export.gif")
	) {
		public void actionPerformed(ActionEvent e) {
			exportDialog.show();
		}
	};
	
	/** Exit the application. */
	protected AbstractAction closeAction = new AbstractAction(
		resources.getString("menu.file.exit"),
		getIcon("exit.gif")
	) {
		public void actionPerformed(ActionEvent e) {
			closeWindow();
		}
	};

	//
	// Project save / load
	//

	/** Clear the project data. */
	protected void clearProject() {

		if (askToSave() == CANCEL) { return; }
		project.clear();
		file = null;
	}

	/** Save the project. */
	protected void saveProject(boolean forceSelection) {

		if ((file == null) || forceSelection) {
			
			JFileChooser fc = new JFileChooser();
			fc.addChoosableFileFilter(new ExtensionFilter("fce", resources.getString("filter.vofce")));

			int returnVal = fc.showDialog(this, resources.getString("menu.file.save"));
			if (returnVal != JFileChooser.APPROVE_OPTION) {
				return;
			}

			file = fc.getSelectedFile();
			
			if (! file.getPath().endsWith(".fce")) {
				file = new File(file.getPath() + ".fce");
			}
		}

		try {
			FileOutputStream f = new FileOutputStream(file);
			project.store(f);
			f.close();
			
		} catch (Exception e) {
			logger.error("MainWindow: Error while saving project, " + e);
			showError(resources.getString("error.cantWrite") + ":\n" + file.getPath());
		}
	}

	/** Load project. */
	protected void loadProject() {

		if (askToSave() == CANCEL) { return; }

		JFileChooser fc = new JFileChooser();
		fc.addChoosableFileFilter(new ExtensionFilter("fce", resources.getString("filter.vofce")));

		int returnVal = fc.showDialog(this, resources.getString("menu.file.open"));
		if (returnVal != JFileChooser.APPROVE_OPTION) {
			return;
		}
		
		file = fc.getSelectedFile();

		try {
			FileInputStream f = new FileInputStream(file);
			project.load(f);
			f.close();
		} catch (Exception e) {
			logger.error("MainWindow: Error while loading project, " + e);
			showError(resources.getString("error.cantRead") + ":\n" + file.getPath());
		}
	}
	
	/** Displays error box. */
	protected void showError(String text) {

		JOptionPane.showMessageDialog(this, text, resources.getString("error"), JOptionPane.ERROR_MESSAGE);
	}
	
	/** Displays the "project modified, save?" question. */
	public int askToSave() {
		
		if (! project.isDirty()) {
			return -1;
		}

		Object[] buttons = {
			resources.getString("button.yes"),
			resources.getString("button.no"),
			resources.getString("button.cancel")
		};

		int result = JOptionPane.showOptionDialog(
			this,
			resources.getString("project.wantToSave"),
			resources.getString("project.saveChanges"),
			JOptionPane.YES_NO_CANCEL_OPTION,
			JOptionPane.QUESTION_MESSAGE,
			null,
			buttons,
			buttons[NO]);

		if (result == YES) {
			saveProject(false);
		}
		
		return result;
	}
	//
	// GraphPanel actions
	// 
	
	/** Reset the viewport. */
	protected AbstractAction resetViewport = new AbstractAction(
		null, getIcon("reset.gif")
	) {
		public void actionPerformed(ActionEvent e) {
			graphPanel.reset();
		}
	};

	/** Move the viewport. */
	protected AbstractAction moveTool = new AbstractAction(
		null, getIcon("move.gif")
	) {
		public void actionPerformed(ActionEvent e) {
			graphPanel.setMouseMode(GraphPanel.MOUSEMODE_MOVE);
		}
	};

	/** Zoom the viewport. */
	protected AbstractAction zoomTool = new AbstractAction(
		null, getIcon("zoom.gif")
	) {
		public void actionPerformed(ActionEvent e) {
			graphPanel.setMouseMode(GraphPanel.MOUSEMODE_ZOOM);
		}
	};
	
	//
	// Some initialisations
	// 
	
	static {
		
		resources = ResourceBundle.getBundle("net.sf.vofce.gui.resources");
	}
}
